package user

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"git.mycaigou.com/gfyx/common/tenant_db"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/constants"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/errcode"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils/auth"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils/captcha"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils/verifycode"
	profitRepo "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/base/profit"
	userRepo "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/base/user"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/remote/gfyx_user_service_remote"
	pb "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/proto/micro_gfyx_oppty_service_proto/micro-gfyx-oppty-service/base"
	tenantPb "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/proto/micro_gfyx_user_service_proto/micro-gfyx-user-service/tenant"
	"git.myscrm.cn/golang/common/ykerrcode"
	"git.myscrm.cn/golang/stark/v4"
	"net/url"
	"strconv"
	"time"
)

type loginService struct {
	userRepo            userRepo.UserRepositoryIface
	userBehaviorRepo    userRepo.UserBehaviorRepositoryIface
	loginRepo           userRepo.LoginRepositoryIface
	profitRepo          profitRepo.ProfitRepositoryIface
	gfyxUserServiceRepo gfyx_user_service_remote.GfyxUserServiceRepositoryIface
}

// NewLoginService 构造函数
func NewLoginService(
	userRepo userRepo.UserRepositoryIface,
	userBehaviorRepo userRepo.UserBehaviorRepositoryIface,
	loginRepo userRepo.LoginRepositoryIface,
	profitRepo profitRepo.ProfitRepositoryIface,
	gfyxUserServiceRepo gfyx_user_service_remote.GfyxUserServiceRepositoryIface,
) LoginServiceIface {
	return &loginService{
		userRepo:            userRepo,
		userBehaviorRepo:    userBehaviorRepo,
		loginRepo:           loginRepo,
		profitRepo:          profitRepo,
		gfyxUserServiceRepo: gfyxUserServiceRepo,
	}
}

// LoginByPassword 账号密码登录
func (l *loginService) LoginByPassword(ctx context.Context, request *pb.LoginByPasswordRequest) (*pb.LoginByPasswordResponse, error) {
	resp := &pb.LoginByPasswordResponse{}

	// 1. 检查账号是否被锁定
	authUtils := auth.NewAuth()
	if authUtils.IsLocked(ctx, request.Account) {
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_ACCOUNT_LOCKED, "账号被锁定，请稍后再尝试")
	}

	// 2. 错误数大于 3 时，校验验证码
	errCnt := authUtils.GetUserLoginErrorCnt(ctx, request.Account)
	if !(captcha.NewCaptcha(ctx).VerifyCaptcha(request.CaptchaId, request.CaptchaAnswer)) {
		return nil, ykerrcode.TogRPCError(errcode.COMMON_VERIFY_CODE_ERROR, "验证码错误")
	}
	errMsg := ""
	// errMsg := fmt.Sprintf("，已失败%d次，失败%d次后账号将被锁定%d分钟", errCnt+1, auth.LOGIN_MAX_ERROR_CNT, auth.LOCK_USER_TIME/60)
	// 3.获取用户信息
	// 检查用户状态 并且获取用户信息
	userEntity, err := l.checkUserStatus(ctx, request.Account)
	if err != nil {
		authUtils.IncrUserLoginErrorCnt(ctx, request.Account)
		stark.Logger.Infof(ctx, "method:GetUserByAccountOrMobile,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, err.Error())
	}

	// 4. 校验账号密码
	if !authUtils.VerifyPassword(request.Password, userEntity.Password, userEntity.Salt) {
		errPwd := true
		// 背景：天际出库平台在出库新租户时，会给admin账号设定初始密码，但由于设置的密码只保存在myuser表，并未同步到gfyx_user，导致两张表的密码不一致
		// 特殊校验：如果是admin账号，则尝试从myuser表读取password和salt，进行校验密码
		if userEntity.IsAdmin == 1 {
			myuserInfo, err := l.userRepo.GetMyUserInfoByUserGuid(ctx, userEntity.Uid)
			if err != nil {
				return nil, err
			}
			if authUtils.VerifyPassword(request.Password, myuserInfo.Password, myuserInfo.Salt) {
				errPwd = false
			}
		}

		if errPwd {
			authUtils.IncrUserLoginErrorCnt(ctx, request.Account)
			// 如果账号密码错误已超过 3 次 就会启动验证码
			errCode := errcode.USER_LOGIN_VERIFY_PASSWORD_ERROR
			if (errCnt + 1) > 3 {
				errCode = errcode.USER_LOGIN_NEED_CAPTCHA
			}
			return nil, ykerrcode.TogRPCError(errCode, "账号或密码错误"+errMsg)
		}
	}

	// 5. 设置登录状态
	// 默认 4 个小时，如果选择记住登录，则 7 天
	loginExpireTime := int64(14400)
	if request.Remember == 1 {
		loginExpireTime = int64(86400 * 7)
	}
	userLoginEntity := &userRepo.GfyxUserLoginPo{
		LoginTime: time.Now(),
		CreatedOn: time.Now(),
		CreatedBy: strconv.Itoa(int(userEntity.Id)),
	}
	_, err = l.setUserLoginInfo(ctx, request.TenantCode, userEntity, userLoginEntity, loginExpireTime, auth.LoginWayPassword)
	if err != nil {
		stark.Logger.Errorf(ctx, "setUserLogin,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}

	// 7. 保存用户行为日志
	_, err = l.SaveLoginBehavior(ctx, userEntity.Id, userRepo.MethodLoginPassword, request, userLoginEntity.Token)
	if err != nil {
		return nil, err
	}

	// 8. 重置错误次数
	authUtils.ResetUserLoginErrorCnt(ctx, request.Account)
	resp.AccessToken = userLoginEntity.Token
	resp.RefreshToken = userLoginEntity.RefreshToken
	resp.ExpireTime = time.Now().Unix() + loginExpireTime
	return resp, nil
}

// LoginByPhone 手机号码验证登录
func (l *loginService) LoginByPhone(ctx context.Context, request *pb.LoginByPhoneRequest) (*pb.LoginByPhoneResponse, error) {
	resp := &pb.LoginByPhoneResponse{}
	// 1. 检查账号是否被锁定
	authUtils := auth.NewAuth()
	if authUtils.IsLocked(ctx, request.Mobile) {
		return resp, ykerrcode.TogRPCError(errcode.USER_LOGIN_ACCOUNT_LOCKED, "账号被锁定，请稍后再尝试")
	}

	// 2. 检查验证码
	verifyUtil := verifycode.NewVerifyCode(ctx)
	// errCnt := authUtils.GetUserLoginErrorCnt(ctx, request.Mobile)
	// errMsg := fmt.Sprintf("，已失败%d次，失败%d次后账号将被锁定%d分钟", errCnt+1, auth.LOGIN_MAX_ERROR_CNT, auth.LOCK_USER_TIME/60)
	errMsg := ""
	if !verifyUtil.CheckAnswer(request.Mobile, request.Code) {
		authUtils.IncrUserLoginErrorCnt(ctx, request.Mobile)
		return resp, ykerrcode.TogRPCError(errcode.USER_LOGIN_SMS_CODE_ERROR, "验证码错误"+errMsg)
	}

	// 3. 检查用户并且获取用户信息
	userEntity, err := l.checkUserStatus(ctx, request.Mobile)
	if err != nil {
		authUtils.IncrUserLoginErrorCnt(ctx, request.Mobile)
		stark.Logger.Infof(ctx, "method:GetUserByMobile,err:", err)
		return resp, nil
	}

	// 5. 设置登录态
	loginExpireTime := int64(14400)
	if request.Remember == 1 {
		loginExpireTime = int64(86400 * 7)
	}
	userLoginEntity := &userRepo.GfyxUserLoginPo{
		LoginTime: time.Now(),
		ClientIP:  utils.GetClientIp(ctx),
		CreatedOn: time.Now(),
		CreatedBy: strconv.Itoa(int(userEntity.Id)),
	}
	_, err = l.setUserLoginInfo(ctx, request.TenantCode, userEntity, userLoginEntity, loginExpireTime, auth.LoginWaySmsCode)
	if err != nil {
		stark.Logger.Errorf(ctx, "setUserLogin,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}

	// 7. 保存用户行为日志
	_, err = l.SaveLoginBehavior(ctx, userEntity.Id, userRepo.MethodLoginSmsCode, request, userLoginEntity.Token)
	if err != nil {
		return nil, err
	}

	// 重置错误次数
	authUtils.ResetUserLoginErrorCnt(ctx, request.Mobile)

	resp.AccessToken = userLoginEntity.Token
	resp.RefreshToken = userLoginEntity.RefreshToken
	resp.ExpireTime = time.Now().Unix() + loginExpireTime
	return resp, nil
}

// Sso 单点登录
func (l *loginService) Sso(ctx context.Context, request *pb.SsoRequest) (*pb.SsoResponse, error) {
	// 检查用户状态
	userEntity, err := l.checkUserStatus(ctx, request.Account)
	if err != nil {
		return nil, err
	}

	// 设置登录态，返回 token
	// 5. 签发 token
	loginExpireTime := int64(86400 * 7)
	userLoginEntity := &userRepo.GfyxUserLoginPo{
		LoginTime: time.Now(),
		CreatedOn: time.Now(),
		CreatedBy: strconv.Itoa(int(userEntity.Id)),
	}
	_, err = l.setUserLoginInfo(ctx, request.TenantCode, userEntity, userLoginEntity, loginExpireTime, auth.LoginWaySso)
	if err != nil {
		stark.Logger.Errorf(ctx, "setUserLogin,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}

	// 7. 保存用户行为日志
	_, err = l.SaveLoginBehavior(ctx, userEntity.Id, userRepo.MethodLoginSso, request, userLoginEntity.Token)
	if err != nil {
		return nil, err
	}

	//  url
	domain := stark.Configer.(stark.ApolloConfigerIface).GetStringValue("gfyx_front_domain", "")
	parse, err := url.Parse(request.PageUri)
	if err != nil {
		stark.Logger.Errorf(ctx, "parse url failed,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_NOT_FOUND, "解析 url 失败")
	}

	// 构建 Url 参数
	urlParams := url.Values{}
	for key, values := range parse.Query() {
		for _, value := range values {
			urlParams.Set(key, value)
		}
	}
	urlParams.Set("access_token", userLoginEntity.Token)
	urlParams.Set("refresh_token", userLoginEntity.RefreshToken)
	query := urlParams.Encode()
	// 需要兼容有带参数的请求
	respUrl := fmt.Sprintf("%s%s?%s", domain, parse.Path, query)
	return &pb.SsoResponse{
		AccessToken:  userLoginEntity.Token,
		RefreshToken: userLoginEntity.RefreshToken,
		ExpireTime:   time.Now().Unix() + loginExpireTime,
		Url:          respUrl,
	}, nil
}

func (l *loginService) RefreshLoginToken(ctx context.Context, request *pb.RefreshTokenRequest) (*pb.RefreshTokenResponse, error) {
	// 校验 refresh token 是否有效 refresh token 存在并且在有效期内
	condition := map[string]interface{}{
		"refresh_token": request.RefreshToken,
		"is_online":     auth.Online,
	}
	loginInfo, err := l.loginRepo.GetLoginInfo(ctx, condition)
	if err != nil {
		return nil, ykerrcode.TogRPCError(errcode.USER_GET_LOGIN_ERROR, "获取登录态失败")
	}
	// 判断 refresh token 是否过期
	if time.Now().After(loginInfo.ExpiredTime) {
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_REFRESH_TOKEN_UNEXPIRE_ERROR, "refresh token 已过期")
	}

	// 删除旧的 token 数据
	authUtil := auth.NewAuth()
	_, _ = authUtil.DelTokenToCache(ctx, loginInfo.Token)
	_, _ = authUtil.DelRefreshTokenToCache(ctx, loginInfo.RefreshToken)

	// 重新设置登录态
	userInfo, err := l.userRepo.GetUserByCondition(ctx, map[string]interface{}{"id": loginInfo.UserID})
	if err != nil {
		stark.Logger.Errorf(ctx, "GetUserByCondition,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, "用户信息不存在")
	}

	// 计算当前有效时间 有效时间 - 当前时间
	loginExpireTime := loginInfo.ExpiredTime.Unix() - time.Now().Unix()
	_, err = l.setUserLoginInfo(ctx, request.TenantCode, userInfo, loginInfo, loginExpireTime, loginInfo.Way)
	if err != nil {
		stark.Logger.Errorf(ctx, "setUserLogin,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}

	return &pb.RefreshTokenResponse{
		Token:        loginInfo.Token,
		RefreshToken: loginInfo.RefreshToken,
		ExpireTime:   loginInfo.ExpiredTime.Unix(),
	}, nil
}

// GetLoginUserInfo 获取登录用户信息，中间件直接从缓存获取，获取不到再调用此接口
func (l *loginService) GetLoginUserInfo(ctx context.Context, request *pb.LoginUserRequest) (*pb.LoginUserInfo, error) {
	// 从数据表 gfyx_user_login 中取登录态
	userLoginInfo, err := l.loginRepo.GetLoginInfoByToken(ctx, request.Token)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetLoginInfoByToken,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_NOT_FOUND, "获取登录状态失败")
	}
	if userLoginInfo.UserID == 0 {
		return nil, ykerrcode.TogRPCError(errcode.COMMON_USER_TOKEN_EXPIRED, "token 不存在，请重新登录")
	}
	// 从用户表获取信息
	userInfo, err := l.userRepo.GetUserByCondition(ctx, map[string]interface{}{"id": userLoginInfo.UserID})
	if err != nil {
		stark.Logger.Errorf(ctx, "GetUserByCondition,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, "用户信息不存在")
	}
	// 重新设置缓存，缓存时间有规则，有效时间 - 当前时间， 最大不能超过 2 小时
	claims, err := l.generateTokenClaims(ctx, userInfo, 0)
	expireTime := userLoginInfo.ExpiredTime.Unix() - time.Now().Unix()
	if expireTime >= 7200 || expireTime <= 0 {
		expireTime = 7200
	}
	authUtil := auth.NewAuth()
	_, err = authUtil.SetTokenToCache(ctx, request.Token, *claims, int(expireTime))
	if err != nil {
		stark.Logger.Errorf(ctx, "SetTokenToCache, token=%s, value=%v, time=%d err=%v", request.Token, *claims, expireTime, err)
		return nil, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_CACHE_ERROR, "设置用户登录态缓存失败")
	}
	// 返回数据
	return &pb.LoginUserInfo{
		Id:            claims.User.UserId,
		Uid:           claims.User.UserGuid,
		Account:       claims.User.Account,
		Name:          claims.User.Name,
		Mobile:        claims.User.Mobile,
		Email:         claims.User.Email,
		Position:      claims.User.Position,
		ResetPassword: int32(claims.User.ResetPassword),
		TenantName:    claims.Tenant.TenantName,
		TenantCode:    claims.Tenant.TenantCode,
		Profits:       claims.Tenant.Profits,
	}, nil
}

// LogoutUser 用户退出登录
func (l *loginService) LogoutUser(ctx context.Context, request *pb.LogoutRequest) (*pb.LogoutResponse, error) {
	// 更新数据库的登录日志
	loginInfo, err := l.loginRepo.GetLoginInfoByToken(ctx, request.Token)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetLoginInfoByToken,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGOUT_TOKEN_ERROR, "剔除用户登录态失败")
	}
	if loginInfo.ID > 0 {
		loginInfo.IsOnline = auth.Offline
		loginInfo.LogoutTime = sql.NullTime{
			Time:  time.Now(),
			Valid: true,
		}
		_, err = l.loginRepo.SaveUserLogin(ctx, loginInfo)
		if err != nil {
			stark.Logger.Errorf(ctx, "UpdateLogin,err:", err)
			return nil, ykerrcode.TogRPCError(errcode.USER_LOGOUT_TOKEN_ERROR, "剔除用户登录态失败")
		}
	}

	// 从缓存里删除用户 token 和 refresh token
	authUtil := auth.NewAuth()
	_, err = authUtil.DelTokenToCache(ctx, request.Token)
	if err != nil {
		stark.Logger.Errorf(ctx, "DelTokenToCache,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGOUT_TOKEN_CACHE_ERROR, "从缓存剔除用户登录态失败")
	}
	_, err = authUtil.DelRefreshTokenToCache(ctx, loginInfo.RefreshToken)
	if err != nil {
		stark.Logger.Errorf(ctx, "DelRefreshTokenToCache,err:", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGOUT_TOKEN_CACHE_ERROR, "从缓存剔除用户登录态失败")
	}

	return new(pb.LogoutResponse), nil
}

// GetResetPasswordToken 获取重设密码 token
func (l *loginService) GetResetPasswordToken(ctx context.Context, request *pb.GetResetPasswordTokenRequest) (*pb.GetResetPasswordTokenResponse, error) {
	// 验证 code 是否正确
	verifyUtil := verifycode.NewVerifyCode(ctx)
	if !verifyUtil.CheckAnswer(request.Mobile, request.Code) {
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_SMS_CODE_ERROR, "验证码错误")
	}

	// 获取验证码
	token, err := auth.NewTempToken(ctx).Get(request.Mobile)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetResetPasswordToken,err:%+v", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_LOGIN_GET_RESET_PASSWOD_TOKEN, "生成临时令牌失效")
	}

	return &pb.GetResetPasswordTokenResponse{
		Token: token,
	}, nil
}

// ResetPasswordByPhone 重设密码
func (l *loginService) ResetPasswordByPhone(ctx context.Context, request *pb.ResetPasswordByPhoneRequest) (*pb.ResetPasswordByPhoneResponse, error) {
	// 临时 token 校验
	verifyRes, err := auth.NewTempToken(ctx).Verify(request.Mobile, request.Token, true)
	if !verifyRes {
		if err != nil {
			stark.Logger.Errorf(ctx, "NewTempToken Verify failed, key:%s, code:%s, err:%+v", request.Mobile, request.Code, err)
		}
		return nil, ykerrcode.TogRPCError(errcode.USER_RESET_PASSWORD_TEMP_TOKEN_ERROR, "临时访问凭证不正确或者已失效")
	}

	// 密码解析
	authUtils := auth.NewAuth()
	password, salt := authUtils.EncryptPassword(request.Password)

	// 查找用户 并重设密码
	userEntity, err := l.userRepo.GetUserByMobile(ctx, request.Mobile)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetUserByMobile,err:%+v", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, "用户不存在")
	}
	userEntity.Password = password
	userEntity.Salt = salt
	userEntity.ModifiedOn = time.Now()
	userEntity.LastSetPasswordTime = sql.NullTime{
		Time:  time.Now(),
		Valid: true,
	}
	_, err = l.userRepo.SaveUser(ctx, userEntity)
	if err != nil {
		stark.Logger.Errorf(ctx, "ResetPasswordByMobile,err:%+v", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, "用户不存在")
	}

	return new(pb.ResetPasswordByPhoneResponse), nil
}

// checkUserStatus 检查用户状态
func (l *loginService) checkUserStatus(ctx context.Context, account string) (*userRepo.GfyxUserPo, error) {
	// 1. 检查用户是否存在
	userEntity, err := l.userRepo.GetUserByAccountOrMobile(ctx, account)
	if err != nil || userEntity.Uid == "" {
		stark.Logger.Infof(ctx, "GetUserByAccountOrMobile", err)
		return nil, ykerrcode.TogRPCError(errcode.USER_ACCOUNT_NOT_EXISIT, "用户不存在")
	}

	// 2. 判断账号是否禁用
	if userEntity.Status != 1 {
		return nil, ykerrcode.TogRPCError(errcode.USER_IS_FORBID, "用户已经被禁用")
	}

	return userEntity, nil
}

// generateTokenClaims 生成 token 的数据载体
func (l *loginService) generateTokenClaims(ctx context.Context, userEntity *userRepo.GfyxUserPo, loginWay int) (*auth.LoginUserInfo, error) {
	isResetPassword := 0
	if userEntity.LastSetPasswordTime.Valid == false {
		isResetPassword = 1
	}
	// 1. 生成主体信息
	loginInfo := auth.LoginUserInfo{
		User: auth.UserInfoItem{
			UserId:        userEntity.Id,
			UserGuid:      userEntity.Uid,
			Account:       userEntity.Account,
			Name:          userEntity.Name,
			Mobile:        userEntity.Mobile,
			Email:         userEntity.Email,
			Position:      userEntity.Position,
			ResetPassword: isResetPassword,
			Way:           loginWay,
		},
		Tenant: auth.TenantInfo{},
	}

	// 2. 获取租户 code
	var err error
	loginInfo.Tenant.TenantCode, err = tenant_db.GetTenantCode(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "grpc GetTenantCode failed, err:%s", err.Error())
		return nil, ykerrcode.TogRPCError(errcode.COMMON_GET_TENANT_ERROR, "获取租户信息失败")
	}
	tenantInfo, err := l.gfyxUserServiceRepo.GetTenantByCode(ctx, &tenantPb.GetTenantByCodeRequest{Code: loginInfo.Tenant.TenantCode})
	if err != nil {
		stark.Logger.Errorf(ctx, "grpc GetTenantInfoByTenantCode failed, err:%s", err.Error())
		return nil, ykerrcode.TogRPCError(errcode.COMMON_GET_TENANT_ERROR, "获取用户名称失败")
	}
	loginInfo.Tenant.TenantName = loginInfo.Tenant.TenantCode
	if tenantInfo != nil {
		loginInfo.Tenant.TenantName = tenantInfo.Name
	}

	// 3. 获取权益数据
	loginInfo.Tenant.Profits = make([]string, 0)
	list, err := l.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "QueryAllEffectiveProfitList,err:", err)
		return nil, err
	}
	for _, profitItem := range list {
		if profitItem.ProfitCode == constants.ProfitClueRadarNationwide ||
			profitItem.ProfitCode == constants.ProfitMarketReport ||
			profitItem.ProfitCode == constants.ProfitClueRadarProvince ||
			profitItem.ProfitCode == constants.ProfitAIOutbound {
			loginInfo.Tenant.Profits = append(loginInfo.Tenant.Profits, profitItem.ProfitCode)
		}
	}

	return &loginInfo, nil
}

// setUserLoginInfo 设置用户登录信息, 返回登录表实体
func (l *loginService) setUserLoginInfo(ctx context.Context, tenantCode string, userEntity *userRepo.GfyxUserPo, userLoginEntity *userRepo.GfyxUserLoginPo, loginExpireTime int64, way int) (bool, error) {
	// 1. 签发 token
	authUtils := auth.NewAuth()
	// 1.1 构建载体
	userTokenClaim, err := l.generateTokenClaims(ctx, userEntity, way)
	if err != nil {
		stark.Logger.Errorf(ctx, "generateTokenClaims,err:", err)
		return false, ykerrcode.TogRPCError(errcode.USER_ISSUE_TOKEN_ERROR, "颁发用户 token 失败")
	}
	// 1.2 签发 token
	token, refreshToken, err := authUtils.IssueNewToken(tenantCode, *userTokenClaim)
	if err != nil {
		stark.Logger.Errorf(ctx, "IssueNewToken,err:", err)
		return false, ykerrcode.TogRPCError(errcode.USER_ISSUE_TOKEN_ERROR, "颁发用户 token 失败")
	}

	// 2. 保存登录态
	// loginExpireTime 是增加的时间戳
	// expireTime 是当前的时间戳 + 增加的时间戳
	//expireDate := time.Now().Unix() + loginExpireTime
	userLoginEntity.Way = way
	userLoginEntity.UserID = userEntity.Id
	userLoginEntity.IsOnline = auth.Online
	userLoginEntity.Token = token
	userLoginEntity.RefreshToken = refreshToken
	userLoginEntity.ExpiredTime = time.Now().Add(time.Second * time.Duration(loginExpireTime))
	userLoginEntity.ModifiedBy = fmt.Sprintf("%d", userEntity.Id)
	userLoginEntity.ModifiedOn = time.Now()
	userLoginEntity.IsDeleted = constants.IsDeletedFalse
	_, err = l.loginRepo.SaveUserLogin(ctx, userLoginEntity)
	if err != nil {
		stark.Logger.Errorf(ctx, "SaveUserLogin,err:", err)
		return false, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录态失败")
	}

	// 3. 设置缓存信息
	// 设置缓存, token 默认 2 个小时失效，需要通过 refresh_token 进行刷新
	authUtil := auth.NewAuth()
	_, err = authUtil.SetTokenToCache(ctx, token, *userTokenClaim, 7200)
	if err != nil {
		stark.Logger.Errorf(ctx, "SetTokenToCache failed, user:%d, err:", userEntity.Id, err)
		return false, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}
	// refresh_token
	_, err = authUtil.SetRefreshTokenToCache(ctx, refreshToken, token, int(loginExpireTime))
	if err != nil {
		stark.Logger.Errorf(ctx, "SetRefreshTokenToCache failed, user:%d, err:", userEntity.Id, err)
		return false, ykerrcode.TogRPCError(errcode.USER_SAVE_LOGIN_ERROR, "保存用户登录失败")
	}

	return true, nil
}

// SaveLoginBehavior 保存用户行为日志
func (l *loginService) SaveLoginBehavior(ctx context.Context, userId int64, loginWay string, request interface{}, token string) (bool, error) {
	requestStr, _ := json.Marshal(request)
	uid := strconv.Itoa(int(userId))
	userBehaviorPo := &userRepo.GfyxUserBehaviorPo{
		Module:     userRepo.ModuleUser,
		Action:     userRepo.ActionLogin,
		UserID:     uid,
		Token:      token,
		Method:     loginWay,
		Params:     string(requestStr),
		CreatedOn:  time.Now(),
		CreatedBy:  uid,
		ModifiedOn: time.Now(),
		ModifiedBy: uid,
		IsDeleted:  0,
	}
	_, err := l.userBehaviorRepo.SaveUserBehavior(ctx, userBehaviorPo)
	if err != nil {
		return false, ykerrcode.TogRPCError(errcode.USER_LOGIN_SAVE_STATUS_ERROR, "保存用户行为日志失败")
	}
	return true, nil
}
